package com.flyqiu.flow.core.manage;

import com.flyqiu.common.tools.StringTools;
import com.flyqiu.flow.api.element.FlyQiuFlowElementManage;
import com.flyqiu.flow.api.element.ProcElementRegisterWrapper;
import com.flyqiu.flow.api.std.ElementActivityHandler;
import com.flyqiu.flow.api.std.ElementCompleteHandler;
import com.flyqiu.flow.api.std.ElementStartHandler;
import com.flyqiu.flow.api.std.FlyQiuFlowObjectFactory;
import com.flyqiu.flow.bpmn.FlyFlowElementConstants;
import com.flyqiu.flow.bpmn.element.*;
import com.flyqiu.flow.core.handler.*;

import java.util.HashMap;
import java.util.Map;

/**
 * 流程元素管理
 */
public class DefaultFlyQiuFlowElementManage extends FlyFlowElementConstants implements FlyQiuFlowElementManage {
    private Map<String, ProcElementRegisterWrapper> defaultProcElement = new HashMap<>();
    private final Map<String, ProcElementRegisterWrapper> procElement = new HashMap<>();

    private final FlyQiuFlowObjectFactory objectFactory;

    public DefaultFlyQiuFlowElementManage(FlyQiuFlowObjectFactory objectFactory) {
        this.objectFactory = objectFactory;
        this.initElement();
    }

    private void initElement() {
        defaultProcElement = new HashMap<>();
        //event
        defaultProcElement.put(FlyFlowElementConstants.ELEMENT_TYPE_START_EVENT, new ProcElementRegisterWrapper(FlyFlowElementConstants.ELEMENT_TYPE_START_EVENT, StartEvent.class, StartEventHandler.class, StartEventHandler.class));
        defaultProcElement.put(FlyFlowElementConstants.ELEMENT_TYPE_END_EVENT, new ProcElementRegisterWrapper(FlyFlowElementConstants.ELEMENT_TYPE_END_EVENT, EndEvent.class, EndEventHandler.class, EndEventHandler.class));
        //task
        defaultProcElement.put(FlyFlowElementConstants.ELEMENT_TYPE_USER_TASK, new ProcElementRegisterWrapper(FlyFlowElementConstants.ELEMENT_TYPE_USER_TASK, UserTask.class, UserTaskHandler.class, UserTaskHandler.class));
        //gateway
        defaultProcElement.put(FlyFlowElementConstants.ELEMENT_TYPE_EXCLUSIVE_GATEWAY, new ProcElementRegisterWrapper(FlyFlowElementConstants.ELEMENT_TYPE_EXCLUSIVE_GATEWAY, ExcludeGateway.class, ExcludeGatewayHandler.class, ExcludeGatewayHandler.class));
        defaultProcElement.put(FlyFlowElementConstants.ELEMENT_TYPE_PARALLEL_GATEWAY, new ProcElementRegisterWrapper(FlyFlowElementConstants.ELEMENT_TYPE_PARALLEL_GATEWAY, ParallelGateway.class, ParallelGatewayHandler.class, ParallelGatewayHandler.class));
        defaultProcElement.put(FlyFlowElementConstants.ELEMENT_TYPE_INCLUSIVE_GATEWAY, new ProcElementRegisterWrapper(FlyFlowElementConstants.ELEMENT_TYPE_INCLUSIVE_GATEWAY, InclusiveGateway.class, InclusiveGatewayHandler.class, InclusiveGatewayHandler.class));

        //sequence_flow
        defaultProcElement.put(FlyFlowElementConstants.ELEMENT_TYPE_SEQUENCE_FLOW, new ProcElementRegisterWrapper(FlyFlowElementConstants.ELEMENT_TYPE_SEQUENCE_FLOW, SequenceFlow.class, SequenceElementHandler.class, SequenceElementHandler.class));
        //process
        defaultProcElement.put(FlyFlowElementConstants.ELEMENT_TYPE_SUB_PROCESS, new ProcElementRegisterWrapper(FlyFlowElementConstants.ELEMENT_TYPE_SUB_PROCESS, SubProcessModel.class, SubProcessHandler.class, SubProcessHandler.class));

    }

    public boolean hasElementHandler(String elementType) {
        if (StringTools.isBlank(elementType)) {
            throw new RuntimeException("elementType is null or empty");
        }
        if (procElement.containsKey(elementType)) {
            return true;
        }
        return defaultProcElement.containsKey(elementType);
    }

    public void registerProcElement(String elementType, Class<? extends BaseProcElement> modelClazz, Class<? extends ElementActivityHandler> actHandlerClazz) {
        registerProcElement(new ProcElementRegisterWrapper(elementType, modelClazz, actHandlerClazz, actHandlerClazz));
    }

    public void registerProcElement(ProcElementRegisterWrapper registerWrapper) {
        if (StringTools.isAllBlank(registerWrapper.elementType())) {
            throw new IllegalArgumentException("registerProcElement elementType cannot be empty or null");
        }
        if (registerWrapper.elementClazz() == null) {
            throw new IllegalArgumentException("registerProcElement modelClazz cannot be null");
        }
        if (registerWrapper.elementStartHandler() == null) {
            throw new IllegalArgumentException("registerProcElement elementStartHandler cannot be null");
        }
        if (registerWrapper.elementCompleteHandler() == null) {
            throw new IllegalArgumentException("registerProcElement elementCompleteHandler cannot be null");
        }
        procElement.put(registerWrapper.elementType(), registerWrapper);
    }


    public ProcElementRegisterWrapper getWrapper(String elementType) {
        if (procElement.containsKey(elementType)) {
            return procElement.get(elementType);
        }
        return defaultProcElement.get(elementType);
    }

    public ElementStartHandler<?> createStartInst(String elementType) {
        ProcElementRegisterWrapper wrapper = getWrapper(elementType);
        return objectFactory.createObject(wrapper.elementStartHandler());
    }

    @Override
    public ElementCompleteHandler createCompleteInst(String elementType) {
        ProcElementRegisterWrapper wrapper = getWrapper(elementType);
        return objectFactory.createObject(wrapper.elementCompleteHandler());
    }

    public BaseProcElement createElement(String elementType) {
        if (StringTools.isEmpty(elementType)) {
            throw new RuntimeException("elementType is null or empty");
        }
        ProcElementRegisterWrapper wrapper = getWrapper(elementType);
        if (wrapper == null) {
            throw new RuntimeException("not find elementClass by elementType:" + elementType);
        }
        return objectFactory.createObject(wrapper.elementClazz());
    }
}
